package com.goldfish666.esp8266;

import com.goldfish666.esp8266.util.AirConditionerHelper;
import com.goldfish666.esp8266.util.SocketHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@SpringBootApplication
public class Esp8266Application {

    private static int counter = 0;

    private static int criticalValue = 0;

    private static int cycle = 0;

    private static final int tolerance = 2;

    private static boolean functionHasSet = false;

    private static List<Byte> currentStandardInput = new ArrayList<>();

    private static List<List<Byte>> representativeList = new ArrayList<>();

    private static Map<List<Byte>, Integer> standardValueMap = new LinkedHashMap<>();


    private static List<Integer> differentIndexList = new ArrayList<>();

    public static void main(String[] args) {

        SocketHelper.buildDatagramSocket(8090);
        DatagramSocket datagramSocket = SocketHelper.getDatagramSocket();
        while (true) {
            //定义数据包,用于存储数据
            byte[] buf = new byte[121];
            DatagramPacket dp = new DatagramPacket(buf, buf.length);
            try {

                datagramSocket.receive(dp);//通过服务的receive方法将收到数据存入数据包中,receive()为阻塞式方法
                //通过数据包的方法获取其中的数据
                String ip = dp.getAddress().getHostAddress();
                log.info("接收到第： " + counter + "个数据。");

                // 获取标准值的map（多次重复按同意功能的按钮，计数）
                getStandardValueMap(dp.getData());

                if (!functionHasSet) {
//                    AirConditionerHelper.setFunction("temperature");
//                    AirConditionerHelper.setFunction("speed");
                    AirConditionerHelper.setFunction("powerSwitch");
                    cycle = AirConditionerHelper.getStateCount();
                    criticalValue = AirConditionerHelper.getMinSize();
                    functionHasSet = true;
                }

                boolean canGetDifferentIndexList = canGetDifferentIndexList();

                if (counter == criticalValue && !canGetDifferentIndexList) {
                    criticalValue = criticalValue + cycle;
                    log.info("调整maxSize到: {}", criticalValue);
                }

                if (canGetDifferentIndexList) {
                    if (counter == criticalValue) {
                        // 获取代表这一功能的众多状态的列表
                        getRepresentativeList();
                        printfRepresentativeList();

                        // 获取值不同的坐标
                        getDifferentIndex();
                        printfDifferentIndex();
                    } else if (counter > criticalValue && inputIsRepresentative()) {
                        printfDifferentIndex();
                        printfCurrentValue();
                    }
                }
                counter++;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static boolean inputIsRepresentative() {
        return representativeList.contains(currentStandardInput);
    }

    private static void printfRepresentativeList() {
        for (List<Byte> list : representativeList) {
            StringBuilder sb = new StringBuilder(2 * list.size());
            for (Byte aByte : list) {
                sb.append(aByte);
                sb.append(" ");
            }
            log.info(sb.toString());
        }
    }

    private static void printfCurrentValue() {
        StringBuilder sb = new StringBuilder(2 * differentIndexList.size());
        for (Integer index : differentIndexList) {
            sb.append(currentStandardInput.get(index));
            sb.append(" ");
        }
        log.info(sb.toString());
    }

    private static void printfDifferentIndex() {
        StringBuilder sb = new StringBuilder(2 * differentIndexList.size());
        for (Integer integer : differentIndexList) {
            sb.append(integer);
            sb.append(" ");
        }
        log.info(sb.toString());
    }

    private static void getDifferentIndex() {
        int length = representativeList.get(0).size();
        for (int i = 0; i < length; i++) {
            byte sum = 0;
            for (List<Byte> x : representativeList) {
                sum = (byte) (sum + x.get(i));
            }
            if (sum > 0 && sum < representativeList.size()) {
                differentIndexList.add(i);
            }
        }
        log.info("差异坐标：");
        printfDifferentIndex();
    }

    private static boolean canGetDifferentIndexList() {
        if (standardValueMap.values().size() <= cycle) {
            return false;
        }
        List<Integer> countList = standardValueMap.values().parallelStream().sorted((o1, o2) -> o2 - o1).collect(Collectors.toList());
        int lastOne = countList.get(cycle - 1);
        int not = countList.get(cycle);
        return lastOne / not > tolerance;
    }

    // 从map里选出频次最高的n个list
    private static void getRepresentativeList() {
        List<Integer> countList = standardValueMap.values().parallelStream().sorted((o1, o2) -> o2 - o1).limit(cycle).collect(Collectors.toList());
        for (List<Byte> byteList : standardValueMap.keySet()) {
            if (countList.contains(standardValueMap.get(byteList))) {
                representativeList.add(byteList);
            }
        }
    }

    private static void reverse(List<Byte> input) {
        int t = input.size() / 8;
        for (int ii = 0; ii < t; ii++) {
            int start = ii * 8 + 1;
            int sum = 2 * start + 7;
            for (int j = start; j < start + 4; j++) {
                byte temp0 = input.get(j);
                byte temp1 = input.get(sum - j);
                input.set(j, temp1);
                input.set(sum - j, temp0);
            }
        }
    }

    private static void getStandardValueMap(byte[] origin) {
        List<Byte> result = new ArrayList<>();

        // 获取到原来的值：消除高低电平变化的影响，消除每位先发低位的影响。
        for (byte currentValue : origin) {
            if (currentValue > 125 || currentValue < 0) {
                result.add((byte) 2);
            } else if (currentValue > 15) {
                result.add((byte) 0);
            } else {
                result.add((byte) 1);
            }
        }
        reverse(result);
        currentStandardInput = result;
        if (ObjectUtils.isEmpty(standardValueMap.get(result))) {
            standardValueMap.put(result, 1);
        } else {
            int count = standardValueMap.get(result);
            standardValueMap.put(result, count + 1);
        }
    }
}
